#include "osFlagGroup.h"

#include "osCpu.h"

#if OS_ENABLE_FLAGGROUP == 1

/*****************************************************************************
 * @function        vFlagGroupInit
 * @brief 			事件标志组初始化.
 *  
 * @param[in] 		fgroup 待初始化的事件标志组控制块.
 * @param[in]       flags 初始化的事件标志.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vFlagGroupInit(FLAGGROUP_T *fgroup, uint32_t flags)
{
    vEventInit(&fgroup->event, eventTypeFlagGroup);
    fgroup->flags = flags;
}



/*****************************************************************************
 * @function        sf_FlagGroupCheckAndComsume
 * @brief 			私有辅助函数，检查并消耗事件标志.
 *  
 * @param[in] 		fgroup 待检查的事件标志组.
 * @param[in]       type 事件标志检查类型.
 * @param[in,out] 	flags 待检查的事件标志，及检查结果存储位置.
 * 
* @return 			OS_NOERROR检查事件匹配，OS_ERROR_NORESOURCE检查事件不匹配.	
 *
 * ***************************************************************************/
static uint32_t sf_FlagGroupCheckAndComsume(FLAGGROUP_T *fgroup, uint32_t type, uint32_t *flags)
{
    uint32_t srcFlag = *flags;                      // 待检查的事件标志
    uint32_t isSet = type & FLAGGROUP_SET;          // 是否置位
    uint32_t isAll = type & FLAGGROUP_ALL;          // 是否全部
    uint32_t isComsume = type & FLAGGROUP_CONSUME;  // 是否消耗事件标志
    
    // 有哪些类型的标志位出现
    // fgroup->flags & srcFlag ： 计算出哪些位置1
    // ~fgroup->flags & srcFlag ： 计算出哪些位置0
    uint32_t calFlag = isSet ? (fgroup->flags & srcFlag) : (~fgroup->flags & srcFlag);
    
    // 所有标志位出现 | 任意标志位出现 满足要求
    if ( ((isAll != 0) && (calFlag == srcFlag)) || ((isAll == 0) && (calFlag != 0)))
    {
        // 是否消耗掉标志位
        if (isComsume)
        {
            if (isSet)
            {
                // 清除为1的标志位，变为0
                fgroup->flags &= ~srcFlag;
            }
            else
            {
                // 清除为0的标志为，变为1
                fgroup->flags |= srcFlag;
            }
        }
        
        // 检查结果
        *flags = calFlag;
        return OS_NOERROR;
    }
    
    *flags = calFlag;
    return OS_ERROR_NORESOURCE;
}


/*****************************************************************************
 * @function        vFlagGroupWait
 * @brief 			等待事件标志组中特定标志.
 *  
 * @param[in] 		fgroup 事件标志组.
 * @param[in]       waitType 等待的类型.
 * @param[in] 	    requestFlag 请求的事件标志.
 * @param[out] 	    resultFlag 等待标志的结果.
 * @param[in] 	    timeout 超时时间.
 * 
 * @return 			等待结果.	
 *
 * ***************************************************************************/
uint32_t vFlagGroupWait(FLAGGROUP_T *fgroup, 
                                uint32_t waitType, 
                                uint32_t requestFlag, 
                                uint32_t *resultFlag, 
                                uint32_t timeout)
{
    uint32_t result;
    uint32_t flags = requestFlag;
    
    uint32_t primask = vTaskEnterCritical();
    
    /* 获取检查的结果*/
    result = sf_FlagGroupCheckAndComsume(fgroup, waitType, &flags);
    /* 检查结果为：事件标志不匹配---> 任务入事件等待队列*/
    if (result != OS_NOERROR)
    {
        curTask->waitFlagType = waitType;               // 等待标志类型
        curTask->eventFlags = requestFlag;              // 请求匹配标志
        vEventWait(&fgroup->event, curTask, (void *)0, OS_TASK_STA_WAIT_FGROUP, timeout);
        
        vTaskExitCritical(primask);
        
        vTaskYield();                                   // 切换任务        
        
        /* 任务就绪后，在此执行*/
        *resultFlag = curTask->eventFlags;              // 获取标志结果
        result = curTask->waitEventResult;              // 请求结果
    }
    /* 标志匹配*/
    else
    {
        *resultFlag = flags;                            // 获取事件标志结果
        vTaskExitCritical(primask);                     
    }
    
    return result;                                      // 成功                    
}



/*****************************************************************************
 * @function        vFlagGroupNoWait
 * @brief 			获取事件标志组中特定的标志.
 *  
 * @param[in] 		fgroup 事件标志组.
 * @param[in]       waitType 等待的类型.
 * @param[in] 	    requestFlag 请求的事件标志.
 * @param[out] 	    resultFlag 等待标志的结果.
 * 
 * @return 			获取结果.	
 *
 * ***************************************************************************/
uint32_t vFlagGroupNoWait(FLAGGROUP_T *fgroup, 
                                uint32_t waitType, 
                                uint32_t requestFlag, 
                                uint32_t *resultFlag)
{
    uint32_t flags = requestFlag;
    uint32_t result;
    uint32_t primask = vTaskEnterCritical();

    result = sf_FlagGroupCheckAndComsume(fgroup, waitType, &flags);
    
    vTaskExitCritical(primask);
    
    *resultFlag = flags;

    return result;
}


/*****************************************************************************
 * @function        vFlagGroupNotify
 * @brief 			通知事件标志组中的特定标志.
 *  
 * @param[in] 		fgroup 事件标志组.
 * @param[in]       isSet 是否设置事件标志组置1.
 * @param[in,out] 	flag 待通知的事件标志.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vFlagGroupNotify(FLAGGROUP_T *fgroup, uint8_t isSet, uint32_t flag)
{
    LIST_T *waitList;
    NODE_T *node;
    NODE_T *nextNode;
    TASK_T *task;
    uint32_t result;
    uint32_t flags;
    uint8_t sched = 0;      // 遍历结束，判断是否需要上下文切换
    
    uint32_t primask = vTaskEnterCritical();
    
    if (isSet)
    {
        fgroup->flags |= flag;
    }
    else
    {
        fgroup->flags &= ~flag;
    }
    
    /* 遍历所有等待任务*/
    waitList = &fgroup->event.waitList;
    for (node = waitList->headNode.nextNode; node != &(waitList->headNode); node = nextNode)
    {
        nextNode = node->nextNode;
        task = container_of(node, TASK_T, taskLinkNode);
        flags = task->eventFlags ;
        
        // 检查事件标志组
        result = sf_FlagGroupCheckAndComsume(fgroup, task->waitFlagType, &flags);
        if (result == OS_NOERROR)
        {
           // 唤醒任务
           task->eventFlags = flags;
           vEventWakeUpTask(&fgroup->event, task, (void *)0, OS_NOERROR);
           sched = 1;
        }
    }
    vTaskExitCritical(primask);
    
    // 有任务就绪，进行上下文切换
    if (sched )
    {
        vTaskYield();
    }
}    



/*****************************************************************************
 * @function        vFlagGroupDestroy
 * @brief 			删除事件标志组中事件等待队列的所有任务.
 *  
 * @param[in] 		fgroup 事件标志组.
 * 
 * @return 			被释放的任务数量.	
 *
 * ***************************************************************************/
uint32_t vFlagGroupDestroy(FLAGGROUP_T *fgroup)
{
    uint32_t count = 0;
    uint32_t primask = vTaskEnterCritical();
    
    count = vEventRemoveAll(&fgroup->event, (void *)0, OS_FORCE_DEL);
    
    vTaskExitCritical(primask);
    
    if (count)
    {
        vTaskYield();
    }
    return count;
}

/*****************************************************************************
 * @function        vFlagGroupGetInfo
 * @brief 			获取事件标志组相关信息.
 *  
 * @param[in] 		fgroup 事件标志组.
 * @param[in]       info 事件标志组信息控制块
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vFlagGroupGetInfo(FLAGGROUP_T *fgroup, FLAGGROUP_INFO_T * info)
{
    uint32_t primask = vTaskEnterCritical();
    
    info->taskCount = vEventWaitCount(&fgroup->event);
    info->flags = fgroup->flags;
    
    vTaskExitCritical(primask);

}

#endif // OS_ENABLE_FLAGGROUP == 1
